using System;
using Framework.Library.Core.Utils.Miscellaneous;

namespace Framework.Library.Core.Utils.Text
{
    /// <summary>
    /// Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer.
    /// </summary>
    internal class StringBuffer
    {
        private static readonly char[] _emptyBuffer = new char[0];
        private char[] _buffer;
        private int _position;

        public StringBuffer()
        {
            _buffer = _emptyBuffer;
        }

        public StringBuffer(int initialSize)
        {
            ValidationUtils.ArgumentNotNegative<int>(initialSize, "initialSize");

            _buffer = new char[initialSize];
        }

        public StringBuffer(char[] internalBuffer)
        {
            ValidationUtils.ArgumentNotNull(internalBuffer, "internalBuffer");

            _buffer = internalBuffer;
            _position = internalBuffer.Length;
        }

        public int Position
        {
            get { return _position; }
            set
            {
                ValidationUtils.ArgumentNotNegative<int>(value, "value");
                _position = value;
            }
        }

        public void Append(char value)
        {
            // ensure buffer array is large enough to contain value
            if (_position + 1 > _buffer.Length)
                EnsureSize(1);

            // set value and increment poisition
            _buffer[_position++] = value;
        }

        public void Append(char[] value)
        {
            if (value == null)
                return;

            // ensure buffer array is large enough to contain value
            if (_position + value.Length > _buffer.Length)
                EnsureSize(value.Length);

            for (int i = 0; i < value.Length; i++)
            {
                _buffer[_position++] = value[i];
            }
        }

        public void Clear()
        {
            // release buffer memory
            _buffer = _emptyBuffer;
            _position = 0;
        }

        private void EnsureSize(int appendLength)
        {
            // double buffer size
            var newBuffer = new char[(_position + appendLength)*2];
            int copyLength = (_buffer.Length < _position) ? _buffer.Length : _position;

            Array.Copy(_buffer, newBuffer, copyLength);

            _buffer = newBuffer;
        }

        public override string ToString()
        {
            return new string(_buffer, 0, _position);
        }
    }
}